﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Amazon.DynamoDB.Model;
using DevToolBox.Cloud.Common;

namespace DevToolBox.Cloud.Amazon
{
    /// <summary>
    /// Class to access a data repository using its metadata as simple
    /// input parameters. Does not use objects (like DynamoDbEF does it)
    /// <remarks>
    /// NOt actively used at the moment
    /// </remarks>
    /// </summary>
    public class DynamoDb : DynamoDbClient, ITableCommand
    {
        public DynamoDb():base()
        {
        }

        #region "Old Funtionality"
        
        public ScanResponse Scan(ScanRequest sr)
        {
            return Client.Scan(sr);
        }

        public object Put(object req)
        {
            return Client.PutItem((PutItemRequest)req);
        }

        public Key PreparePaging(string lastKey, string lastRange)
        {
            if (lastKey != null && lastRange != null)
            {
                Key qk = new Key();
                qk.WithHashKeyElement(new AttributeValue().WithN(lastKey));
                qk.WithRangeKeyElement(new AttributeValue().WithN(lastRange));
                return qk;
            }

            return null;
        }

        public object ExecuteQuery(string tableName, string hashValue, string lastHashValue, string lastRangeValue, 
                            int recordCount)
        {
            QueryRequest queryReq = new QueryRequest().WithHashKeyValue(new AttributeValue().WithN(hashValue));           
            return ExecuteQueryWorker(queryReq, recordCount, lastHashValue, lastRangeValue, tableName);
        }

        public object ExecuteQuery(string tableName, string hashValue, string hashRange, 
            string rangeOperator, string lastHashValue, string lastRangeValue, int recordCount)
        {
            

            QueryRequest queryReq = new QueryRequest().WithHashKeyValue(new AttributeValue().WithS(hashValue));
            if (hashRange != null)
            {
                Condition itemIdCond = new Condition().WithAttributeValueList(
                                           new AttributeValue().WithS(hashRange));
                itemIdCond.WithComparisonOperator(rangeOperator);
                queryReq.WithRangeKeyCondition(itemIdCond);
            }

            return ExecuteQueryWorker(queryReq, recordCount, lastHashValue, lastRangeValue, tableName);
        }

        public object ExecuteQuery(string tableName, int hashValue, int hashRange,
            string rangeOperator, string lastHashValue, string lastRangeValue, int recordCount)
        {
            QueryRequest queryReq = new QueryRequest().WithHashKeyValue(
                                                       new AttributeValue().WithN(Convert.ToString(hashValue)));
            if (hashRange != null)
            {
                Condition itemIdCond = new Condition().WithAttributeValueList(
                                           new AttributeValue().WithN(Convert.ToString(hashRange)));
                itemIdCond.WithComparisonOperator(rangeOperator);
                queryReq.WithRangeKeyCondition(itemIdCond);
            }

            return ExecuteQueryWorker(queryReq, recordCount, lastHashValue, lastRangeValue, tableName);
        }

        private object ExecuteQueryWorker(QueryRequest queryReq, int recordCount, 
            string lastHashValue, string lastRangeValue, string tableName)
        {
            queryReq.WithTableName(tableName);
            if (lastHashValue != null && lastRangeValue != null)
            {
                queryReq.ExclusiveStartKey = PreparePaging(lastHashValue, lastRangeValue);
            }

            if (recordCount != -1)
            {
                queryReq.Limit = recordCount;
            }

            QueryResponse response = null;
            List<Dictionary<string, AttributeValue>> responseList = new List<Dictionary<string, AttributeValue>>();
            do
            {
                response = Client.Query(queryReq);
                responseList.AddRange(response.QueryResult.Items);
                queryReq.ExclusiveStartKey = response.QueryResult.LastEvaluatedKey;

                if (recordCount != -1 && responseList.Count == recordCount)
                    break;

            } while (response.QueryResult.LastEvaluatedKey != null);
            response.QueryResult.Items = responseList;



            return response;
        }

        public object ExecuteScan(string tableName, string rangeName, string rangeValue,
            string oper, string lastHashValue, string lastRangeValue)
        {
            ScanRequest scanRequest = new ScanRequest();
            scanRequest.TableName = tableName;

            Dictionary<string, Condition> conditions = new Dictionary<string, Condition>();
            Condition nameCond = new Condition().WithAttributeValueList(new AttributeValue().WithS(rangeValue));
            nameCond.WithComparisonOperator(oper);
            conditions[rangeName] = nameCond;
            scanRequest.ScanFilter = conditions;
            scanRequest.ExclusiveStartKey = PreparePaging(lastHashValue, lastRangeValue);

            ScanResponse scanRes = Client.Scan(scanRequest);
            return scanRes;
        }

        #endregion

        public void Save(List<RowEntity> entities)
        {
            foreach (RowEntity entity in entities)
            {
                Save(entity);
            }
        }

        public void Save(RowEntity entity)
        {
            string tableName=entity.GetTableName();
            Dictionary<string, object> values=entity.GetValues();
            Dictionary<string, DbType> attMetaData = entity.GetMetadata();

            var pairList = new List<KeyValuePair<string, AttributeValueUpdate>>();
            Key uniqueKey = new Key();

            foreach (string attKey in values.Keys)
            {
                var attVal = new AttributeValueUpdate().WithAction("ADD");

                switch (attKey)
                {
                    case TableInfo.HashKeyName:
                        uniqueKey.HashKeyElement = ParseValue(attMetaData[attKey], values[attKey]);
                        break;
                    case TableInfo.RangeKeyName:
                        uniqueKey.RangeKeyElement = ParseValue(attMetaData[attKey], values[attKey]);
                        break;
                    default:
                        attVal.Value = ParseValue(attMetaData[attKey], values[attKey]);
                        pairList.Add(new KeyValuePair<string, AttributeValueUpdate>(attKey, attVal));
                        break;
                }
            }

            var updateRequest = new UpdateItemRequest()
                         .WithTableName(tableName)
                         .WithKey(uniqueKey)
                         .WithAttributeUpdates(pairList.ToArray());
            var updateResult = base.Client.UpdateItem(updateRequest);
        }

        private AttributeValue ParseValue(DbType type, object value)
        {
            switch (type)
            {
                case DbType.Int16:
                case DbType.Int32:
                case DbType.Int64:
                case DbType.Decimal:
                case DbType.Double:
                case DbType.UInt16:
                case DbType.UInt32:
                case DbType.UInt64:
                default:
                    return new AttributeValue()
                           .WithN(Convert.ToString(value));
                case DbType.DateTime:
                case DbType.String:
                    return new AttributeValue()
                           .WithS(Convert.ToString(value));
            }
        }
    }
}
